home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Tool Chest / Dev.CD Feb 97 TC.toast / Sample Code / Development Tools & Languages / AppsToGo / DTS.Lib / File2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-22  |  32.5 KB  |  1,103 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         DTS.Lib
  5. ** File:            file2.c
  6. ** Some code from:  Traffic Light 2.0 version, by Keith Rollin & John Harvey
  7. ** Modified by:     Eric Soldan
  8. **
  9. ** Copyright © 1990-1993 Apple Computer, Inc.
  10. ** All rights reserved.
  11. */
  12.  
  13. /* You may incorporate this sample code into your applications without
  14. ** restriction, though the sample code has been provided "AS IS" and the
  15. ** responsibility for its operation is 100% yours.  However, what you are
  16. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  17. ** after having made changes. If you're going to re-distribute the source,
  18. ** we require that you make it clear in the source that the code was
  19. ** descended from Apple Sample Code, but that you've made changes. */
  20.  
  21.  
  22.  
  23. /*****************************************************************************/
  24.  
  25.  
  26.  
  27. #include "DTS.Lib2.h"
  28. #include "DTS.Lib.protos.h"
  29.  
  30. #ifndef __ERRORS__
  31. #include <Errors.h>
  32. #endif
  33.  
  34. #ifndef __FILES__
  35. #include <Files.h>
  36. #endif
  37.  
  38. #if __MOVIESUPPORT__
  39.  
  40. #ifndef __IMAGECOMPRESSION__
  41. #include <ImageCompression.h>
  42. #endif
  43.  
  44. #ifndef __MOVIES__
  45. #include <Movies.h>
  46. #endif
  47.  
  48. #endif
  49.  
  50. #ifndef __PACKAGES__
  51. #include <Packages.h>
  52. #endif
  53.  
  54. #ifndef __RESOURCES__
  55. #include <Resources.h>
  56. #endif
  57.  
  58. #ifndef __TOOLUTILS__
  59. #include <ToolUtils.h>
  60. #endif
  61.  
  62.  
  63.  
  64. /*****************************************************************************/
  65.  
  66.  
  67.  
  68. extern OSType        gDocCreator;    /* Initialized to gSignature by Init. */
  69. extern long            gAppWindowAttr;    /* Initialized by app in Window.c. */
  70. extern long            gQTVersion;        /* Initialized by Utilities.c in InitQuickTime. */
  71.  
  72. extern TreeObjHndl    gWindowFormats;    /* If non-nil, then get descriptions of windows from here. */
  73.  
  74. extern short        gTypeListLen;    /* Initialized by app in File.c. */
  75. extern OSType        *gTypeListPtr;    /* Initialized by app in File.c. */
  76.  
  77. FileFilterUPP        gSFGetFileFilterUPP;
  78.  
  79. static OSErr        Create_OpenFile(FSSpec *file, short *refNum, OSType sftype);
  80.  
  81.  
  82.  
  83. /*****************************************************************************/
  84. /*****************************************************************************/
  85.  
  86. #ifdef applec
  87. #pragma segment ATGFile
  88. #endif
  89.  
  90. /*****************************************************************************/
  91. /*****************************************************************************/
  92.  
  93.  
  94.  
  95. /* This function disposes of the document.  It checks to see if a file is
  96. ** currently open for the document.  If it is, then the document is closed.
  97. ** Once there is no open file for the document, the memory occupied by the
  98. ** document is released. */
  99.  
  100. OSErr    DisposeDocument(FileRecHndl frHndl)
  101. {
  102.     OSErr        err, err2;
  103.     short        refNum;
  104.     WindowPtr    window;
  105.     Movie        movie;
  106.  
  107.     err = noErr;
  108.  
  109.     if (frHndl) {
  110.  
  111.         if ((refNum = (*frHndl)->fileState.refNum) != kInvalRefNum) {        /* If file open... */
  112.  
  113.             if ((*frHndl)->fileState.sfType == MovieFileType)        /* If movie file... */
  114.                 err = CloseMovieFile(refNum);                        /* Close it. */
  115.  
  116.             else                                                    /* If not movie file... */
  117.                 err = FSClose(refNum);                                /* Close it.       */
  118.         }
  119.  
  120.         CloseDocResFile(frHndl);                    /* Close resource fork, if opened. */
  121.  
  122.         window = (*frHndl)->fileState.window;
  123.         err2    = DoFreeDocument(frHndl);            /* Free all application-specific document ram. */
  124.  
  125.         movie = (*frHndl)->fileState.movie;
  126.         if (movie)
  127.             DisposeMovie(movie);                    /* If we have a movie, dispose it. */
  128.  
  129.         if (window)
  130.             SetWRefCon(window, (long)nil);            /* Mark window as no longer having a document. */
  131.  
  132.         if (!err)
  133.             err = err2;
  134.  
  135.         DisposeHandle((Handle)frHndl);                /* Release memory for the document handle. */
  136.     }
  137.  
  138.     return(err);
  139. }
  140.  
  141.  
  142.  
  143. /*****************************************************************************/
  144.  
  145.  
  146.  
  147. /* This function creates a new document.  A handle is created as the
  148. ** reference to the document.  Header information is placed in this handle.
  149. ** The application-specific data follows this header information.  The
  150. ** handle is returned (or nil upon failure), and typically the handle is
  151. ** then stored in the refCon field of the window.  Note that this is a
  152. ** convention, and is not mandatory.  This allows a document to exist that
  153. ** has no window.  A document with no window is useful when the application
  154. ** is called from the finder in response to a print request.  The document
  155. ** can be loaded and printed without involving a window on the screen. */
  156.  
  157. OSErr    NewDocument(FileRecHndl *returnHndl, OSType sftype, Boolean incTitleNum)
  158. {
  159.     long                size;
  160.     FileRecHndl            frHndl;
  161.     FileRecPtr            frPtr;
  162.     Str255                untitled;
  163.     StringPtr            pstr;
  164.     OSErr                err;
  165.     short                i;
  166.     Movie                movie;
  167.     TreeObjHndl            wobj;
  168.     PositionWndProcPtr    windowPlacementProc;
  169.     static short        untitledCount;
  170.  
  171.     if (!sftype) {
  172.         if (returnHndl == (FileRecHndl *)-1)
  173.             --untitledCount;
  174.         if (!returnHndl)
  175.             untitledCount = 0;
  176.         return(noErr);
  177.     }
  178.  
  179.     if (returnHndl)
  180.         *returnHndl = nil;
  181.  
  182.     err  = memFullErr;                /* Assume that we will fail. */
  183.  
  184.     size = InitDocumentSize(sftype);
  185.         /* Call the application and ask it how big the frHndl should be for
  186.         ** this document type.  We can't know, so we'll ask. */
  187.  
  188.     frHndl = (FileRecHndl)NewHandleClear(size);
  189.     if (frHndl) {
  190.         /* Create (or try to) the frHndl, initialized to all 0's */
  191.  
  192.         if (returnHndl)
  193.             *returnHndl = frHndl;
  194.  
  195.         windowPlacementProc = StaggerWindow;
  196.         wobj = nil;
  197.         if (gWindowFormats) {
  198.             for (i = 0; i < (*gWindowFormats)->numChildren; ++i) {
  199.                 wobj = GetChildHndl(gWindowFormats, i);
  200.                 if (sftype == mDerefWFMT(wobj)->sfType) {
  201.                     windowPlacementProc = nil;
  202.                         /* If using 'WFMT' descriptions, this will be fully determined later. */
  203.                     pcpy(untitled, (StringPtr)mDerefWFMT(wobj)->title);
  204.                     if (untitled[0])
  205.                         if (untitled[1] == ' ')
  206.                             untitled[0] = 0;
  207.                                 /* If title begins with a space, it's just a comment. */
  208.  
  209.                     break;
  210.                 }
  211.                 wobj = nil;
  212.             }
  213.         }
  214.         if (!wobj) {
  215.             for (i = gTypeListLen; --i;) if (sftype == gTypeListPtr[i]) break;
  216.                 /* Walk the typeList to find this file type.  We are interested in
  217.                 ** where we find the entry.  The position we find it is used as a
  218.                 ** string number into the rDefaultTitles STR# resource.  We get
  219.                 ** an individual string from this location.  This allows us to
  220.                 ** have different default titles for different document types. */
  221.     
  222.             for (++i; i; i--) {
  223.                 GetIndString(untitled, rDefaultTitles, i);
  224.                 if (untitled[0]) break;        /* Quit if we succeeded at getting one. */
  225.             }
  226.         }
  227.  
  228.         (*frHndl)->fileState.modNum = GetModNum();
  229.             /* In case GetModNum gets moved to another code segment, set this value
  230.             ** prior to dereferencing frHndl into frPtr. */
  231.  
  232.         frPtr = *frHndl;
  233.         frPtr->fileState.sfType                  = sftype;
  234.         frPtr->fileState.modTick                 = TickCount();
  235.         frPtr->fileState.refNum                  = kInvalRefNum;
  236.         frPtr->fileState.resRefNum               = kInvalRefNum;
  237.         frPtr->fileState.fss.vRefNum             = kInvalVRefNum;
  238.         frPtr->fileState.windowID                = rWindow;
  239.             /* The above sets the fileState constants for the document.  Note
  240.             ** that we use a default 'WIND' ID for the expected window resource.
  241.             ** This can be changed later, if the default isn't good enough. */
  242.  
  243.         if (wobj) {
  244.             frPtr->fileState.windowID      = mDerefWFMT(wobj)->windowID;
  245.             frPtr->fileState.attributes    = mDerefWFMT(wobj)->attributes;
  246.             frPtr->fileState.hScrollIndent = mDerefWFMT(wobj)->hScrollIndent;
  247.             frPtr->fileState.vScrollIndent = mDerefWFMT(wobj)->vScrollIndent;
  248.             frPtr->fileState.leftSidebar   = mDerefWFMT(wobj)->leftSidebar;
  249.             frPtr->fileState.topSidebar    = mDerefWFMT(wobj)->topSidebar;
  250.                 /* Set window attributes as described in resource. */
  251.         }
  252.         else
  253.             frPtr->fileState.attributes = gAppWindowAttr;
  254.                 /* Set window attributes for the main document type. If the document
  255.                 ** is not the main type, then the application's InitDocument function
  256.                 ** will have to change it. */
  257.  
  258.         frPtr->fileState.getDocWindow        = windowPlacementProc;
  259.         frPtr->fileState.adjustMenuItemsProc = AdjustMenuItems;
  260.         frPtr->fileState.doMenuItemProc      = DoMenuItem;
  261.         switch (frPtr->fileState.attributes & (kwIsPalette | kwIsModalDialog)) {
  262.             case kwIsPalette:
  263.                 frPtr->fileState.calcFrameRgnProc  = PaletteCalcFrameRgn;
  264.                 frPtr->fileState.contentClickProc  = PaletteContentClick;
  265.                 frPtr->fileState.contentKeyProc    = PaletteContentKey;
  266.                 frPtr->fileState.drawFrameProc     = PaletteDrawFrame;
  267.                 frPtr->fileState.freeDocumentProc  = PaletteFreeDocument;
  268.                 frPtr->fileState.freeWindowProc    = PaletteFreeWindow;
  269.                 frPtr->fileState.imageProc         = PaletteImageDocument;
  270.                 frPtr->fileState.initContentProc   = PaletteInitContent;
  271.                 frPtr->fileState.readDocumentProc  = nil;
  272.                 frPtr->fileState.resizeContentProc = PaletteResizeContent;
  273.                 frPtr->fileState.scrollFrameProc   = PaletteScrollFrame;
  274.                 frPtr->fileState.undoFixupProc     = PaletteUndoFixup;
  275.                 frPtr->fileState.windowCursorProc  = PaletteWindowCursor;
  276.                 frPtr->fileState.writeDocumentProc = nil;
  277.                 break;
  278.             case kwIsModalDialog:
  279.                 frPtr->fileState.calcFrameRgnProc    = DialogCalcFrameRgn;
  280.                 frPtr->fileState.contentClickProc    = DialogContentClick;
  281.                 frPtr->fileState.contentKeyProc      = DialogContentKey;
  282.                 frPtr->fileState.drawFrameProc       = DialogDrawFrame;
  283.                 frPtr->fileState.freeDocumentProc    = DialogFreeDocument;
  284.                 frPtr->fileState.freeWindowProc      = DialogFreeWindow;
  285.                 frPtr->fileState.imageProc           = DialogImageDocument;
  286.                 frPtr->fileState.initContentProc     = DialogInitContent;
  287.                 frPtr->fileState.readDocumentProc    = nil;
  288.                 frPtr->fileState.resizeContentProc   = DialogResizeContent;
  289.                 frPtr->fileState.scrollFrameProc     = DialogScrollFrame;
  290.                 frPtr->fileState.undoFixupProc       = DialogUndoFixup;
  291.                 frPtr->fileState.windowCursorProc    = DialogWindowCursor;
  292.                 frPtr->fileState.writeDocumentProc   = nil;
  293.                 frPtr->fileState.adjustMenuItemsProc = DialogAdjustMenuItems;
  294.                 frPtr->fileState.doMenuItemProc      = DialogDoMenuItem;
  295.                 break;
  296.             default:
  297.                 frPtr->fileState.calcFrameRgnProc  = CalcFrameRgn;
  298.                 frPtr->fileState.contentClickProc  = ContentClick;
  299.                 frPtr->fileState.contentKeyProc    = ContentKey;
  300.                 frPtr->fileState.drawFrameProc     = DrawFrame;
  301.                 frPtr->fileState.freeDocumentProc  = FreeDocument;
  302.                 frPtr->fileState.freeWindowProc    = FreeWindow;
  303.                 frPtr->fileState.imageProc         = ImageDocument;
  304.                 frPtr->fileState.initContentProc   = InitContent;
  305.                 frPtr->fileState.readDocumentProc  = ReadDocument;
  306.                 frPtr->fileState.resizeContentProc = ResizeContent;
  307.                 frPtr->fileState.scrollFrameProc   = ScrollFrame;
  308.                 frPtr->fileState.undoFixupProc     = UndoFixup;
  309.                 frPtr->fileState.windowCursorProc  = WindowCursor;
  310.                 frPtr->fileState.writeDocumentProc = WriteDocument;
  311.                 break;
  312.         }
  313.  
  314.         frPtr->fileState.windowSizeBounds.left   = kMinWindowWidth;
  315.         frPtr->fileState.windowSizeBounds.top    = kMinWindowHeight;
  316.         frPtr->fileState.windowSizeBounds.right  = kMaxWindowWidth;
  317.         frPtr->fileState.windowSizeBounds.bottom = kMaxWindowHeight;
  318.             /* Default min/max window size for growIcon. */
  319.  
  320.         pstr = frPtr->fileState.fss.name;
  321.         pcpy(pstr, untitled);
  322.         if (pstr[0]) {
  323.             if (incTitleNum)
  324.                 ++untitledCount;
  325.             pcatdec(pstr, untitledCount);
  326.                 /* Create the default document title.  It is stored in the FSSpec,
  327.                 ** as we can't place it in the window title.  We don't have a window
  328.                 ** yet to "title".  This will happen later.  The title is gotten from
  329.                 ** the FSSpec, so we are effectively done. */
  330.         }
  331.  
  332.         err = InitDocument(frHndl);
  333.             /* Call the application for any additional document initialization.
  334.             ** Other handles may need to be allocated.  The default values above
  335.             ** may be incorrect for a certain document type.  This gives the
  336.             ** application a chance to change any defaults that are incorrect. */
  337.  
  338.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  339.             InitQuickTime();
  340.             if (!gQTVersion) return(paramErr);
  341.             movie = NewMovie(newMovieActive);
  342.             err   = GetMoviesError();
  343.             if (!err) {
  344.                 ClearMovieChanged(movie);
  345.                 (*frHndl)->fileState.movie = movie;
  346.             }
  347.         }
  348.  
  349.         if (err) {
  350.             DisposeHandle((Handle)frHndl);
  351.             if (returnHndl)
  352.                 *returnHndl = nil;
  353.                     /* If the application couldn't complete the document
  354.                     ** initialization, pitch the handle. */
  355.         }
  356.     }
  357.  
  358.     return(err);
  359. }
  360.  
  361.  
  362.  
  363. /*****************************************************************************/
  364.  
  365.  
  366.  
  367. OSErr    OpenDocument(FileRecHndl *result, FSSpecPtr fileToOpen, char permission)
  368. {
  369.     StandardFileReply    reply;
  370.     short                refNum;
  371.     FileRecHndl            frHndl, ff;
  372.     WindowPtr            ww;
  373.     OSErr                err;
  374.     FSSpec                myFileSpec, fss;
  375.     DialogPtr            openDialog;
  376.     short                item;
  377.     FInfo                finderInfo;
  378.     Boolean                openMovie;
  379.     Movie                movie;
  380.     static SFTypeList    typeList = {MovieFileType};
  381.  
  382.     *result = nil;        /* Assume we will fail. */
  383.  
  384.     openMovie = false;
  385.     if (fileToOpen == kOpenMovie) {
  386.         if (!gQTVersion) return(paramErr);        /* Can't do movies without QuickTime. */
  387.         fileToOpen = nil;
  388.         openMovie  = true;
  389.     }
  390.  
  391.     if (!fileToOpen) {
  392.         if (openMovie) {
  393.             StandardGetFilePreview(0L, 1, typeList, &reply);
  394.             if (reply.sfGood)
  395.                 myFileSpec = reply.sfFile;
  396.             else
  397.                 return(userCanceledErr);    /* User canceled. */
  398.         }
  399.         else {
  400.             if (DisplayGetFile(&reply, gTypeListLen, gTypeListPtr))    /* Let user pick file. */
  401.                 myFileSpec = reply.sfFile;                            /* User's choice.       */
  402.             else
  403.                 return(userCanceledErr);    /* User canceled. */
  404.         }
  405.     }
  406.     else {
  407.         err = HGetFInfo(fileToOpen->vRefNum, fileToOpen->parID, fileToOpen->name, &finderInfo);
  408.         if (err) return(err);
  409.         reply.sfType = finderInfo.fdType;        /* OSType of file. */
  410.         myFileSpec   = *fileToOpen;                /* Pre-designated file to open. */
  411.     }
  412.  
  413.     err = NewDocument(&frHndl, reply.sfType, false);
  414.     if (err) return(err);
  415.         /* We couldn't create an empty document, so give it up. */
  416.  
  417.     err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  418.     if (err == paramErr)
  419.         err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  420.  
  421.     if (err == permErr) {
  422.         permission = fsRdPerm;
  423.         err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  424.         if (err == paramErr)
  425.             err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  426.         if (!err)
  427.             (*frHndl)->fileState.readOnly = true;
  428.     }
  429.  
  430.     if (err == opWrErr) {
  431.  
  432.         for (ww = nil; ((ww = GetNextWindow(ww, 0)) != nil);) {
  433.             ff  = (FileRecHndl)GetWRefCon(ww);
  434.             fss = (*ff)->fileState.fss;
  435.             if (
  436.                 (myFileSpec.vRefNum == fss.vRefNum) &&
  437.                 (myFileSpec.parID   == fss.parID) &&
  438.                 (!pcmp(myFileSpec.name, fss.name))
  439.             ) {
  440.                 *result = ff;
  441.                 return(noErr);
  442.             }
  443.         }
  444.  
  445.         ParamText(myFileSpec.name, nil, nil, nil);
  446.         openDialog = GetCenteredDialog(rOpenReadOnly, nil, nil, (WindowPtr)-1L);
  447.         if (!openDialog) {
  448.             DisposeDocument(frHndl);
  449.             return(err);
  450.         }
  451.  
  452.         OutlineDialogItem(openDialog, kOpenYes);
  453.         DoSetCursor(&qd.arrow);
  454.         UnhiliteWindows();
  455.         ModalDialog(gKeyEquivFilterUPP, &item);
  456.         DisposeDialog(openDialog);
  457.         HiliteWindows();
  458.         if (item != kOpenYes) {
  459.             DisposeDocument(frHndl);
  460.             return(userCanceledErr);
  461.         }
  462.  
  463.         (*frHndl)->fileState.readOnly = true;
  464.         permission = fsRdPerm;
  465.         err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  466.         if (err == paramErr)
  467.             err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  468.     }
  469.  
  470.     if (err) {
  471.         DisposeDocument(frHndl);
  472.         return(err);
  473.     }
  474.  
  475.     if ((*frHndl)->fileState.sfType == MovieFileType) {
  476.         FSClose(refNum);                            /* Close file, as it wasn't opened as movie. */
  477.         movie = (*frHndl)->fileState.movie;
  478.         if (movie) {
  479.             DisposeMovie(movie);                    /* NewDocument created an empty movie, */
  480.             (*frHndl)->fileState.movie = nil;        /* so first get rid of it. */
  481.         }
  482.         err = OpenMovieFile(&myFileSpec, &refNum, permission);
  483.         if (err) {
  484.             DisposeDocument(frHndl);
  485.             return(err);
  486.         }
  487.     }
  488.  
  489.     (*frHndl)->fileState.fss    = myFileSpec;
  490.     (*frHndl)->fileState.refNum = refNum;
  491.  
  492.     err = DoReadDocument(frHndl);
  493.     if (err) {
  494.         DisposeDocument(frHndl);
  495.         return(err);
  496.     }
  497.  
  498.     *result = frHndl;
  499.     return(noErr);
  500. }
  501.  
  502.  
  503.  
  504. /*****************************************************************************/
  505.  
  506.  
  507.  
  508. OSErr    SaveDocument(FileRecHndl frHndl, WindowPtr window, short saveMode)
  509. {
  510.     Str255                closeOrQuit;
  511.     short                item, refNum, resID, resWasOpen;
  512.     long                createFlags;
  513.     StandardFileReply    reply;
  514.     OSErr                err;
  515.     Movie                movie;
  516.     Boolean                doPrompt;
  517.     DialogPtr            saveDialog;
  518.  
  519.     err = noErr;
  520.  
  521. /*    When entering, saveMode is set to the menu command number of the
  522. **    the item that prompted this. Current settings are kSave, kSaveAs,
  523. **    kClose, and kQuit. */
  524.  
  525.     if (saveMode != kSaveAs) {                                /* If not save as...                  */
  526.         if (!(*frHndl)->fileState.docDirty) {                /* If file clean...                      */
  527.             if ((*frHndl)->fileState.refNum)                /* If document has a file...          */
  528.                 if (!(*frHndl)->fileState.readOnly)            /* If we are allowed to touch file... */
  529.                     if ((*frHndl)->fileState.attributes & kwOpenAtOldLoc)
  530.                         if (!((*frHndl)->fileState.attributes & kwRuntimeOnlyDoc))
  531.                             DoWriteDocumentHeader(frHndl);
  532.                                 /* Write out document location and print record information. */
  533.                                 /* Ignore errors, as saving the location is a bonus. */
  534.             return(noErr);                                /* Consider it saved. */
  535.         }
  536.     }
  537.  
  538.     pcpy(reply.sfFile.name, (*frHndl)->fileState.fss.name);
  539.  
  540.     if ((saveMode == kClose) || (saveMode == kQuit)) {
  541.         /* If implicit save... */
  542.  
  543.         GetIndString(closeOrQuit, rFileIOStrings,
  544.                      (saveMode == kClose) ? sWClosing : sQuitting);
  545.         ParamText(reply.sfFile.name, closeOrQuit, nil, nil);
  546.  
  547.         saveDialog = GetCenteredDialog(rYesNoCancel, nil, window, (WindowPtr)-1L);
  548.  
  549.         if (saveDialog) {
  550.             OutlineDialogItem(saveDialog, kSaveYes);
  551.             DoSetCursor(&qd.arrow);
  552.             UnhiliteWindows();
  553.             ModalDialog(gKeyEquivFilterUPP, &item);
  554.             DisposeDialog(saveDialog);
  555.             HiliteWindows();
  556.         }
  557.         else
  558.             item = kSaveNo;
  559.                 /* If the dialog isn't displayed, then AppleScript doesn't want it to.
  560.                 ** In this case, we were probably AppleScripted the whole time, so
  561.                 ** the document is an AppleScript-produced document.  The script is
  562.                 ** done with the document, so ditch the document. */
  563.  
  564.         if (item != kSaveYes) {
  565.             err = noErr;
  566.             if (item == kSaveCanceled)
  567.                 err = userCanceledErr;
  568.             return(err);
  569.         }
  570.     }
  571.  
  572.     doPrompt = (
  573.         (saveMode == kSaveAs) ||
  574.         ((*frHndl)->fileState.refNum == kInvalRefNum)
  575.     );
  576.  
  577.     if (doPrompt) {
  578.         /* Prompt with SFGetFile if doing a Save As or have never saved before. */
  579.  
  580.         if (!DisplayPutFile(&reply)) return(userCanceledErr);    /* User canceled the save. */
  581.  
  582.         resWasOpen = (*frHndl)->fileState.resRefNum;
  583.         if ((*frHndl)->fileState.sfType != MovieFileType) {
  584.             if ((*frHndl)->fileState.refNum != kInvalRefNum) {
  585.                 CloseDocResFile(frHndl);        /* Close resource fork, if opened. */
  586.                 FSClose((*frHndl)->fileState.refNum);
  587.             }            /* Close the old file.  Don't respond to any error here because
  588.                         ** the user may be trying to do a save-as because their old file
  589.                         ** is bad.  If we fail to close the old file, and then respond
  590.                         ** to the error, the user won't get the opportunity to save
  591.                         ** their document to a new file. */
  592.  
  593.             (*frHndl)->fileState.refNum      = kInvalRefNum;
  594.             (*frHndl)->fileState.fss.vRefNum = kInvalVRefNum;
  595.             err = Create_OpenFile(&reply.sfFile, &refNum, (*frHndl)->fileState.sfType);
  596.             if (err) return(err);
  597.         }
  598.         else {
  599.             createFlags = createMovieFileDeleteCurFile;
  600.             err = CreateMovieFile(&reply.sfFile, gDocCreator, 0, createFlags, &refNum, nil);
  601.             if (err) return(err);
  602.             resID = 0;
  603.             err = AddMovieResource((*frHndl)->fileState.movie, refNum, &resID, nil);
  604.             if (err) return(err);
  605.             (*frHndl)->fileState.movieResID = resID;
  606.         }
  607.  
  608.         (*frHndl)->fileState.fss    = reply.sfFile;        /* This is the new file. */
  609.         (*frHndl)->fileState.refNum = refNum;
  610.  
  611.         if (resWasOpen) {
  612.             UseDocResFile(frHndl, &resWasOpen, fsRdWrPerm);
  613.             UseResFile(resWasOpen);
  614.         }
  615.  
  616.         if (window)
  617.             NewWindowTitle(window, nil);
  618.     }
  619.     else {
  620.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  621.             movie  = (*frHndl)->fileState.movie;
  622.             refNum = (*frHndl)->fileState.refNum;
  623.             resID  = (*frHndl)->fileState.movieResID;
  624.             err = UpdateMovieResource(movie, refNum, resID, nil);
  625.             if (err) return(err);
  626.         }
  627.     }
  628.  
  629.     err = DoWriteDocument(frHndl);
  630.     if (err) return(err);
  631.  
  632.     (*frHndl)->fileState.docDirty = false;
  633.     (*frHndl)->fileState.readOnly = false;
  634.     return(noErr);
  635. }
  636.  
  637.  
  638.  
  639. /*****************************************************************************/
  640.  
  641.  
  642.  
  643. /* ConvertOldToNewSFReply
  644. **
  645. ** struct StandardFileReply {            struct SFReply {
  646. **     Boolean     sfGood;                <-    Boolean good;
  647. **     Boolean     sfReplacing;        <-    Boolean copy;
  648. **     OSType         sfType;                <-    OSType fType;
  649. **     FSSpec        sfFile;
  650. **                     vRefNum;        <-    real vRefnum from (short vRefNum)
  651. **                     parID;            <-    real dirID from (short vRefNum)
  652. **                     name;            <-    Str63 fName;
  653. **     ScriptCode    sfScript;            <-    iuSystemScript
  654. **     short         sfFlags;            <-    0
  655. **     Boolean     sfIsFolder;            <-    false
  656. **     Boolean     sfIsVolume;            <-    false
  657. **     long        sfReserved1;        <-    0
  658. **     short        sfReserved2;        <-    0
  659. ** };                                    };
  660. */
  661.  
  662. void    ConvertOldToNewSFReply(SFReply *oldReply, StandardFileReply *newReply)
  663. {
  664.     OSErr        err;
  665.     long        ignoredProcID;
  666.  
  667.     newReply->sfGood        = oldReply->good;
  668.     newReply->sfReplacing    = oldReply->copy;        /* Correct assignment? */
  669.     newReply->sfType        = oldReply->fType;
  670.  
  671.     err = GetWDInfo(oldReply->vRefNum,
  672.                     &newReply->sfFile.vRefNum,
  673.                     &newReply->sfFile.parID,
  674.                     &ignoredProcID);
  675.     pcpy(newReply->sfFile.name, oldReply->fName);
  676.  
  677.     /* Punt on the rest... */
  678.     newReply->sfScript        = iuSystemScript;
  679.     newReply->sfFlags        = 0;
  680.     newReply->sfIsFolder    = false;
  681.     newReply->sfIsVolume    = false;
  682.     newReply->sfReserved1    = 0;
  683.     newReply->sfReserved2    = 0;
  684. }
  685.  
  686.  
  687.  
  688. /*****************************************************************************/
  689.  
  690.  
  691.  
  692. /* Opens the file specified by the passed FSSpec, creating it if it doesn't
  693. ** already exist. Returns the refnum of the open file to the application.
  694. ** File Manager errors are reported and returned. */
  695.  
  696. static OSErr    Create_OpenFile(FSSpec *file, short *refNum, OSType sftype)
  697. {
  698.     OSErr    err;
  699.  
  700.     err = HCreate(file->vRefNum, file->parID, file->name, gDocCreator, sftype);
  701.     if (err == dupFNErr) {
  702.  
  703.         /* The user already told Standard File to replace the old file
  704.            so let's get rid of it. */
  705.  
  706.         HDelete(file->vRefNum, file->parID, file->name);
  707.  
  708.         /* Try creating it again. */
  709.         err = HCreate(file->vRefNum, file->parID, file->name, gDocCreator, sftype);
  710.     }
  711.  
  712.     if (!err) {
  713.         err = HOpenDF(file->vRefNum, file->parID, file->name, fsRdWrPerm, refNum);
  714.         if (err == paramErr)
  715.             err = HOpen(file->vRefNum, file->parID, file->name, fsRdWrPerm, refNum);
  716.         if (err)
  717.             HDelete(file->vRefNum, file->parID, file->name);
  718.     }
  719.  
  720.     return(err);
  721. }
  722.  
  723.  
  724.  
  725. /*****************************************************************************/
  726.  
  727.  
  728.  
  729. /* Simple routine to display a list of files with our file type. */
  730.  
  731. Boolean    DisplayGetFile(StandardFileReply *reply, short typeListLen, SFTypeList typeList)
  732. {
  733.     Point        where = {100, 100};
  734.     SFReply        oldReply;
  735.  
  736.     if (gSystemVersion >= 0x0700)        /* If new standard file available... */
  737.         StandardGetFile(gSFGetFileFilterUPP, typeListLen, typeList, reply);
  738.  
  739.     else {
  740.         SFGetFile(where, "\pSelect a document to open.",
  741.                          gSFGetFileFilterUPP, typeListLen, typeList, nil, &oldReply);
  742.         ConvertOldToNewSFReply(&oldReply, reply);
  743.     }
  744.  
  745.     return(reply->sfGood);
  746. }
  747.  
  748.  
  749.  
  750. /*****************************************************************************/
  751.  
  752.  
  753.  
  754. /* Displays the StandardFile PutFile dialog box. Fills out the passed reply
  755. ** record, and returns the sfGood field as a result. */
  756.  
  757. Boolean    DisplayPutFile(StandardFileReply *reply)
  758. {
  759.     Str255        prompt;
  760.     Point        where = {100, 100};
  761.     SFReply        oldReply;
  762.  
  763.     GetIndString(prompt, rFileIOStrings, sSFprompt);
  764.  
  765.     if (gSystemVersion >= 0x0700)    /* If new standard file available... */
  766.         StandardPutFile(prompt, reply->sfFile.name, reply);
  767.     else {
  768.         SFPutFile(where, prompt, reply->sfFile.name, nil, &oldReply);
  769.         ConvertOldToNewSFReply(&oldReply, reply);
  770.     }
  771.  
  772.     return(reply->sfGood);
  773. }
  774.  
  775.  
  776.  
  777. /*****************************************************************************/
  778.  
  779.  
  780.  
  781. /* Use the resource fork for the designated document file.  This function
  782. ** also returns the old CurResFile, so you can set it back when you are done.
  783. ** Simply call this function, whether or not you have a resource fork.  If
  784. ** there isn't a resource fork, then one will be created.  If there is one,
  785. ** but it isn't open yet, it will be opened.  If it is already opened, it
  786. ** sets it as the current resource fork.  What more do you want? */
  787.  
  788. #define fcbFlgRBit 0x200
  789.  
  790. OSErr    UseDocResFile(FileRecHndl frHndl, short *oldRes, char perm)
  791. {
  792.     OSErr        err;
  793.     FSSpec        fss;
  794.     short        res, vrn;
  795.     long        pid;
  796.     FCBPBRec    pb;
  797.     CInfoPBRec    resOpen;
  798.  
  799.     if (oldRes)
  800.         *oldRes = CurResFile();
  801.  
  802.     if ((res = (*frHndl)->fileState.resRefNum) != kInvalRefNum) {
  803.         UseResFile(res);        /* If the resource fork already open, use it. */
  804.         return(ResError());
  805.     }
  806.  
  807.     SetMem(&pb, 0, sizeof(FCBPBRec));            /* Make most of the param block happy. */
  808.     pb.ioRefNum = res = (*frHndl)->fileState.refNum;
  809.     err = PBGetFCBInfoSync(&pb);
  810.     if (err) return(err);
  811.     if (pb.ioFCBFlags & fcbFlgRBit) {
  812.         (*frHndl)->fileState.resRefNum = res;
  813.         UseResFile(res);
  814.         return(ResError());
  815.     }
  816.  
  817.     fss = (*frHndl)->fileState.fss;
  818.     vrn = fss.vRefNum;
  819.     pid = fss.parID;
  820.  
  821.     SetMem(&resOpen, 0, sizeof(CInfoPBRec));    /* Make most of the param block happy. */
  822.     resOpen.hFileInfo.ioVRefNum = vrn;
  823.     resOpen.hFileInfo.ioDirID   = pid;
  824.     resOpen.hFileInfo.ioNamePtr = fss.name;
  825.     err = PBGetCatInfoSync(&resOpen);
  826.     if (err) return(err);
  827.     if (resOpen.hFileInfo.ioFlAttrib & 0x04) {
  828.         /* The 0x04 is to look at the bit that says whether or not the resource fork is
  829.         ** already open.  Why did we do this?  To keep from re-opening a resource fork.
  830.         ** Reopening it actually works, but things get a bit ugly when it is closed.  The
  831.         ** one-and-only reference to the open resource fork would get closed if we thought
  832.         ** we opened it.  This keeps us from doing way-bad things later. */
  833.         UseResFile(resOpen.hFileInfo.ioFRefNum);
  834.         return(ResError());
  835.     }
  836.  
  837.     if ((*frHndl)->fileState.readOnly)
  838.         if (perm  & fsWrPerm) perm ^= fsWrPerm;
  839.  
  840.     SetResLoad(false);
  841.     res = HOpenResFile(vrn, pid, fss.name, perm);
  842.     err = ResError();
  843.     SetResLoad(true);
  844.         /* Try opening the resource fork. */
  845.  
  846.     if (err) {
  847.         if (err != eofErr) return(err);                    /* Some errors we can't handle here. */
  848.         HCreateResFile(vrn, pid, fss.name);                /* No resource fork, so create one.  */
  849.         err = ResError();
  850.         if (err) return(err);                            /* Error creating the resource fork. */
  851.         res = HOpenResFile(vrn, pid, fss.name, perm);    /* Now that it exists, open it.  */
  852.         err = ResError();                                /* Return whatever error occurs. */
  853.     }
  854.  
  855.     if (!err) {        /* If no error, then we can use the resource fork. */
  856.         (*frHndl)->fileState.resRefNum = res;
  857.         UseResFile(res);
  858.         err = ResError();
  859.     }
  860.  
  861.     return(err);
  862. }
  863.  
  864.  
  865.  
  866. /*****************************************************************************/
  867.  
  868.  
  869.  
  870. /* If there is a resource fork open for this document, this closes it. */
  871.  
  872. OSErr    CloseDocResFile(FileRecHndl frHndl)
  873. {
  874.     short    res;
  875.  
  876.     if ((res = (*frHndl)->fileState.resRefNum) == kInvalRefNum) return(noErr);
  877.         /* If it was never opened, then there's nothing to close. */
  878.  
  879.     if ((*frHndl)->fileState.refNum == res) {
  880.         (*frHndl)->fileState.resRefNum = kInvalRefNum;
  881.         return(noErr);
  882.     }
  883.  
  884.     CloseResFile(res);                                    /* Close the resource fork. */
  885.     (*frHndl)->fileState.resRefNum = kInvalRefNum;        /* Mark it as closed. */
  886.  
  887.     return(ResError());
  888. }
  889.  
  890.  
  891.  
  892. /*****************************************************************************/
  893.  
  894.  
  895.  
  896. long    GetModNum(void)
  897. {
  898.     static    modNum = 0;
  899.  
  900.     return(++modNum);
  901. }
  902.  
  903.  
  904.  
  905. /*****************************************************************************/
  906.  
  907.  
  908.  
  909. /* This function returns the state of the document.  If the document
  910. ** is dirty, then true is returned.  If the document is clean, then false
  911. ** is returned. */
  912.  
  913. Boolean    GetDocDirty(FileRecHndl frHndl)
  914. {
  915.     if (frHndl) return((*frHndl)->fileState.docDirty);
  916.     return(false);
  917. }
  918.  
  919.  
  920.  
  921. /*****************************************************************************/
  922.  
  923.  
  924.  
  925. /* This function returns the state of the window's document.  If the document
  926. ** is dirty, then true is returned.  If the document is clean, or the window
  927. ** has no document, then false is returned. */
  928.  
  929. Boolean    GetWindowDirty(WindowPtr window)
  930. {
  931.     if (IsAppWindow(window)) return(GetDocDirty((FileRecHndl)GetWRefCon(window)));
  932.     return(false);
  933. }
  934.  
  935.  
  936.  
  937. /*****************************************************************************/
  938.  
  939.  
  940.  
  941. void    SetDocDirty(FileRecHndl frHndl)
  942. {
  943.     if (frHndl) {
  944.         if (!((*frHndl)->fileState.attributes & kwRuntimeOnlyDoc)) {
  945.             (*frHndl)->fileState.docDirty = true;
  946.             (*frHndl)->fileState.modNum   = GetModNum();
  947.             (*frHndl)->fileState.modTick  = TickCount();
  948.         }
  949.     }
  950. }
  951.  
  952.  
  953.  
  954. /*****************************************************************************/
  955.  
  956.  
  957.  
  958. void    SetWindowDirty(WindowPtr window)
  959. {
  960.     if (IsAppWindow(window))
  961.         SetDocDirty((FileRecHndl)GetWRefCon(window));
  962. }
  963.  
  964.  
  965.  
  966. /*****************************************************************************/
  967.  
  968.  
  969.  
  970. /*  The SetDefault function sets the default volume and directory to the volume specified
  971. **  by newVRefNum and the directory specified by newDirID. The current default volume 
  972. **  and directory are returned in oldVRefNum and oldDir and should be used to restore 
  973. **  things to their previous condition *as soon as possible* with the RestoreDefault 
  974. **  function. These two functions are designed to be used as a wrapper around
  975. **  Standard C I/O routines where the location of the file is implied to be the
  976. **  default volume and directory. In other words, this is how you should use these
  977. **  functions:
  978. **
  979. **        err = SetDefault(newVRefNum, newDirID, &oldVRefNum, &oldDirID);
  980. **        if (!err)
  981. **            {
  982. **                -- call the Stdio functions like remove, rename, tmpfile, fopen,   --
  983. **                -- freopen, etc. or non-ANSI extentions like fdopen, fsetfileinfo, --
  984. **                -- create, open, unlink, etc. here!                                   --
  985. **
  986. **                err = RestoreDefault(oldVRefNum, oldDirID);
  987. **            }
  988. **
  989. **  By using these functionw as a wrapper, you won't need to open a working directory 
  990. **  (because they use HSetVol) and you won't have to worry about the effects of using
  991. **  HSetVol (documented in Technical Note #140: Why PBHSetVol is Dangerous 
  992. **  and in the Inside Macintosh: Files book in the description of the HSetVol and 
  993. **  PBHSetVol functions) because the default volume/directory is restored before 
  994. **  giving up control to code that might be affected by HSetVol.
  995. ** Use this and the below call instead of the old-style FSpSetWD and FSpResetWD. */
  996.  
  997. OSErr    SetDefault(short newVRefNum, long newDirID, short *oldVRefNum, long *oldDirID)
  998. {
  999.     OSErr    err;
  1000.  
  1001.     err = HGetVol(nil, oldVRefNum, oldDirID);
  1002.         /* Get the current default volume/directory. */
  1003.  
  1004.     if (!err)
  1005.         err = HSetVol(nil, newVRefNum, newDirID);
  1006.             /* Set the new default volume/directory */
  1007.  
  1008.     return(err);
  1009. }
  1010.  
  1011.  
  1012.  
  1013. /*****************************************************************************/
  1014.  
  1015.  
  1016.  
  1017. OSErr    RestoreDefault(short oldVRefNum, long oldDirID)
  1018. {
  1019.     OSErr    err;
  1020.     short    defaultVRefNum;
  1021.     long    defaultDirID;
  1022.     long    defaultProcID;
  1023.  
  1024.     err = GetWDInfo(oldVRefNum, &defaultVRefNum, &defaultDirID, &defaultProcID);
  1025.         /* Determine if the default volume was a wdRefNum. */
  1026.  
  1027.     if (!err) {
  1028.         /* Restore the old default volume/directory, one way or the other. */
  1029.  
  1030.         if (defaultDirID != fsRtDirID)
  1031.             err = SetVol(nil, oldVRefNum);
  1032.                 /* oldVRefNum was a wdRefNum - use SetVol */
  1033.         else
  1034.             err = HSetVol(nil, oldVRefNum, oldDirID);
  1035.                 /* oldVRefNum was a real vRefNum - use HSetVol */
  1036.     }
  1037.  
  1038.     return(err);
  1039. }
  1040.  
  1041.  
  1042.  
  1043. /*****************************************************************************/
  1044. /*****************************************************************************/
  1045. /*****************************************************************************/
  1046.  
  1047.  
  1048.  
  1049. /* Get the vRefNum and dirID of a file, which is its location. */
  1050.  
  1051. OSErr    GetFileLocation(short refNum, short *vRefNum, long *dirID, StringPtr fileName)
  1052. {
  1053.     FCBPBRec pb;
  1054.     OSErr err;
  1055.  
  1056.     pb.ioNamePtr = fileName;
  1057.     pb.ioVRefNum = 0;
  1058.     pb.ioRefNum  = refNum;
  1059.     pb.ioFCBIndx = 0;
  1060.  
  1061.     err = PBGetFCBInfoSync(&pb);
  1062.  
  1063.     if (vRefNum)
  1064.         *vRefNum = pb.ioFCBVRefNum;
  1065.     if (dirID)
  1066.         *dirID = pb.ioFCBParID;
  1067.  
  1068.     return(err);
  1069. }
  1070.  
  1071.  
  1072.  
  1073. /*****************************************************************************/
  1074.  
  1075.  
  1076.  
  1077. /* After getting a resource, you can't actually be sure that it came from the current
  1078. ** resource file.  Even if you make a call such as Get1Resource, starting with system 7.1,
  1079. ** you can't really be sure that it came from the current resource file.  (The resource
  1080. ** files may be overridden, or they may be flagged to be extended, as is the case with
  1081. ** font files.)  This checks to see that the resource actually came from the current
  1082. ** resource file.  If it didn't, then the handle returned is nil, and the error returned
  1083. ** is resNotFound.  (You probably don't need this function unless you are doing some kind
  1084. ** of resource-editing function.) */
  1085.  
  1086. OSErr    CurResOnly(Handle *hndl)
  1087. {
  1088.     short    cr, hr;
  1089.     OSErr    err;
  1090.  
  1091.     cr  = CurResFile();
  1092.     hr  = HomeResFile(*hndl);
  1093.     err = ResError();
  1094.     if (hr == -1) err = resNotFound;
  1095.     if (!hr)      hr  = 2;                /* Home res file is the system file. */
  1096.  
  1097.     if (cr != hr) *hndl = nil;
  1098.     return(err);
  1099. }
  1100.  
  1101.  
  1102.  
  1103.